/*
 * @Author: duapple
 * @Date: 2021-09-28 14:01:30
 * @LastEditTime: 2022-03-10 10:40:16
 * @LastEditors: duapple
 * @Description: Add description
 * @FilePath: \go_clang_format\main.go
 * Copyright (c) 2011 duapple - All rights reserved.
 */
package main

import (
	"fmt"
	log "fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"runtime"
)

const (
	CFORMAT_CONFIG_DIR = "/usr/share/cformat/"
)

func main() {

	clang_format_process(os.Args...)
}

func clang_format_process(args ...string) {
	conf_dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
	work_dir, _ := os.Getwd()

	if conf_dir == work_dir {
		if os := runtime.GOOS; os == "linux" {
			conf_dir = filepath.Clean(CFORMAT_CONFIG_DIR)
		} else if os == "windows" {
			conf_dir = filepath.Clean("D:\\Program Files\\app")
		}
	}

	// fmt.Println("Runtime OS: ", runtime.GOOS)

	work_dir_config := filepath.Join(work_dir, ".clang-format")
	var config string = "config/.clang-format"
	conf_dir_config := filepath.Join(conf_dir, filepath.Clean(config)) /* 使用clean生成与平台无关的路径 */

	/* 根据不同的平台选择不同的格式化工具 */
	var tool_dir string
	switch os := runtime.GOOS; os {
	case "windows":
		tool_dir = filepath.Join(conf_dir, "bin/clang-format.exe")
	case "linux":
		tool_dir = filepath.Join(conf_dir, "bin/clang-format")
	}

	if _, err := os.Stat(tool_dir); err != nil {
		log.Println("Error: no such file: ", tool_dir)
		return
	}

	// is_custom := flag.Bool("nc", false, "custom argurement lists")
	// flag.Parse()
	if len(os.Args) <= 1 {
		log.Println("Usage: \ncformat file.c file1.c\nor cformat -nc (= clang-format[.exe] )\nor cformat -a\n")
		return
	}

	var args_count int = 0
	// 使用-cu参数，请删除true和修改os.Args[2:]
	if os.Args[1] != "-nc" {
		var filesList []string = make([]string, 0)

		if os.Args[1] == "-a" {
			files, _ := ioutil.ReadDir("./")
			for _, f := range files {
				file_ext := path.Ext(f.Name())
				if file_ext != ".c" && file_ext != ".cpp" && file_ext != ".h" {
					continue
				}
				filesList = append(filesList, f.Name())
			}
		} else {
			for _, f := range os.Args[1:] {
				filesList = append(filesList, f)
			}
		}

		print_args(filesList...)

		if _, err := os.Stat(work_dir_config); err == nil {
			log.Println(work_dir_config)
			log.Println(".clang-format has existed.")
			return
		}

		/* 复制配置文件当工作目录中 */
		if _, err := copy(conf_dir_config, work_dir_config); err != nil {
			log.Println(err)
		}

		ch := make(chan int)
		for _, arg := range filesList {
			file_ext := path.Ext(arg)
			if file_ext != ".c" && file_ext != ".cpp" && file_ext != ".h" {
				continue
			}
			go clang_format_exec(ch, tool_dir, work_dir, arg)
			args_count = args_count + 1
		}

		// 阻塞在此，等待所有goroutine完成
		for i := 0; i < args_count; i++ {
			<-ch
		}
		os.Remove(work_dir_config)
		return
	} else {
		cmd := exec.Command(tool_dir, os.Args[2:]...) /* 不使用自定义参数时，直接调用clang-format */
		output, err := cmd.CombinedOutput()
		if err != nil {
			log.Println(err)
		}
		fmt.Print(string(output))
		return
	}
}

func print_args(args ...string) {
	log.Println("----------args list--------------")
	for _, arg := range args {
		log.Println(arg)
	}
	log.Println("-------------end-----------------")
}

func clang_format_exec(ch chan int, tool_dir, work_dir, file string) {
	log.Println("format ", file)
	cmd := exec.Command(tool_dir, "-i", file)
	err := cmd.Run()
	if err != nil {
		log.Println(err)
	}
	ch <- 1
}

func copy(src, dst string) (int64, error) {
	sourceFileStat, err := os.Stat(src)
	if err != nil {
		return 0, err
	}
	if !sourceFileStat.Mode().IsRegular() {
		return 0, fmt.Errorf("%s is not a regular file", src)
	}

	source, err := os.Open(src)
	if err != nil {
		return 0, err
	}
	defer source.Close()

	destination, err := os.Create(dst)
	if err != nil {
		return 0, err
	}
	defer destination.Close()

	nBytes, err := io.Copy(destination, source)
	return nBytes, err
}
